Revision:
<header-component></header-component> <script> const headerTemplate = document.createElement('template'); headerTemplate.innerHTML = ` <style> .header{text-align: center;} h3{color: blue; font-size: 1.5vw;} </style> <div> <div class="header"> <h3> Header - my initial reflections with respect to web components </h3> </div> </div>` class Header extends HTMLElement { constructor() { super(); } connectedCallback(){ const shadowRoot = this.attachShadow({ mode: 'open' }); shadowRoot.appendChild(headerTemplate.content); } } customElements.define('header-component', Header); </script>
<my-component></my-component> <script> class MyComponent extends HTMLElement { constructor() { super() this.attachShadow({mode: 'open'}).innerHTML = ` <style> span {color: darkblue;} </style> <span>The color of this sentence should be darkblue</span> ` } } customElements.define('my-component', MyComponent) </script>
<div> <first-component></first-component> <second-component></second-component><br><br> </div> <script> class FirstComponent extends HTMLElement { constructor() { super() this.attachShadow({ mode: 'open' }).innerHTML = ` <style> :host{--background: lightgreen;} span{background: var(--background);} </style> <span>Hi, there. This is the first component!</span><br><br> ` } } class SecondComponent extends HTMLElement { constructor() { super() this.attachShadow({ mode: 'open' }).innerHTML = ` <style> :host{--background: lightblue;} span{background: var(--background);} </style> <span>Hello, this is the second component!</span> ` } } customElements.define('first-component', FirstComponent) customElements.define('second-component', SecondComponent) </script>
<div> <first-component-a></first-component-a><br><br> <second-component-a></second-component-a> </div> <script> class FirstComponentA extends HTMLElement { constructor() { super() this.attachShadow({ mode: 'open' }).innerHTML = ` <style> :host{--background: lightgreen;} span{background: var(--background);} </style> <span>Hello, this is the "FIRST" component!</span> ` } } class SecondComponentA extends HTMLElement { constructor() { super() this.attachShadow({ mode: 'open' }).innerHTML = ` <style> :host{--background: lightblue;} span{background: var(--background);} </style> <span>Hello, this is the "SECOND" component!</span> ` } } customElements.define('first-component-a', FirstComponentA) customElements.define('second-component-a', SecondComponentA) </script>
<div> <div>Default: <custom-component></custom-component> </div><br> <div>Dark: <custom-component class="dark"></custom-component> </div><br> <div>Light: <custom-component class="light"></custom-component> </div><br> </div> <script> class CustomComponent extends HTMLElement { constructor() { super() this.attachShadow({ mode: 'open' }).innerHTML = ` <style> :host, :host(.light) {background: white; color: black;} :host(.dark) {background: black; color: white;} @media (prefers-color-scheme: dark) { :host {background: black; color: white;} } </style> <span>Hello world!</span> ` } } customElements.define('custom-component', CustomComponent) </script>
<div> <label-uppercase value="Welcome to Web Components"></label-uppercase><br><br> </div> <script> class UpperCaseLabel extends HTMLElement { get value() { return this.getAttribute("value"); } set value(newValue) { this.setAttribute("value", newValue); } connectedCallback() { let stringContent = (this.value && null !== this.value) ? this.value : ""; this.innerHTML = `<label>${stringContent.toUpperCase()}</label>`; } } customElements.define("label-uppercase", UpperCaseLabel); </script>
<div> <template id="my-drop-down"> <style> .menu-wrap{width: 20.8334vw; display: block; background-color: transparent; font-family: 'ExtraLight'; user-select: none; margin: 20px 40px;} .menu-label{font-size: 1vw; color: rgba(227, 141, 39, .8);padding: 1% 0% 2.5% 7%; } .menu-head{border: 1px solid #e38d27;} .menu-head, .menu-body {background-color: rgba(0, 0, 0, .6);} .menu-head, ::slotted(.menu-option) {min-height: 2.2vw;} .current-choice{color: #fff; height: 2.2vw; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; padding: .7vw .1vw .1vw 1.7vw;} .current-choice, ::slotted(.menu-option){text-transform: capitalize; font-size: 1.2vw;} .menu-body{max-height: 13vw; overflow-y: scroll; overflow-x: hidden;} ::slotted(.menu-option) {color: rgba(255, 255, 255, .3); position: relative; padding-left: 1.7vw; display: flex; align-items: center; transition: padding .5s linear, background-color .25s linear, color .25s linear;} ::slotted(.menu-option::after){content: ''; width: 100%; height: 1px;background-color: rgba(255, 255, 255, .15); position: absolute; bottom: 0; left: 0; } ::slotted(.menu-option:hover){background-color: #e38d27; color: #fff;padding-left: 12%;} .menu-body::-webkit-scrollbar {width: 2.292vw;} .menu-body::-webkit-scrollbar-thumb {background-color: #e38d27; border: .78125vw solid rgba(0, 0, 0, 0); background-clip: padding-box;} .menu-body::-webkit-scrollbar-track {background-color: #111; border: .78125vw solid black;} </style> <div id="my-menu" class="menu-wrap"> <div class="menu-label">Choose your language</div> <div class="menu-head"><div class="current-choice">English</div></div> <div id="menu-body" class="menu-body"> <slot></slot> </div> </div> </template> <drop-down> <div class="menu-option">English</div> <div class="menu-option">Deutsch</div> <div class="menu-option">Espanol</div> <div class="menu-option">Magyar</div> <div class="menu-option">Dansk</div> <div class="menu-option">Euskara</div> <div class="menu-option">Hrvatski</div> <div class="menu-option">Italiano</div> </drop-down> <drop-down> <div class="menu-option">English</div> <div class="menu-option">Deutsch</div> </drop-down> <drop-down> <div class="menu-option">English</div> <div class="menu-option">Deutsch</div> <div class="menu-option">Espanol</div> <div class="menu-option">Magyar</div> <div class="menu-option">Dansk</div> <div class="menu-option">Euskara</div> <div class="menu-option">Hrvatski</div> <div class="menu-option">Italiano</div> <div class="menu-option">English</div> <div class="menu-option">Deutsch</div> <div class="menu-option">Espanol</div> <div class="menu-option">Magyar</div> <div class="menu-option">Dansk</div> <div class="menu-option">Euskara</div> <div class="menu-option">Hrvatski</div> <div class="menu-option">Italiano</div> <div class="menu-option">English</div> <div class="menu-option">Deutsch</div> </drop-down> </div> <script> class DropDown extends HTMLElement { constructor() { super(); } connectedCallback() { // init the component let shadowDOM = this.attachShadow({ mode: "open" }); let template = document.getElementById("my-drop-down"); let templateHtml = template.content.cloneNode(true); shadowDOM.appendChild(templateHtml); // define the menu, menu body and hide it let menu = shadowDOM.getElementById("my-menu"); let menuHead = menu.children[1]; let menuBody = menu.children[2]; menuBody.style.display = "none"; // toggle the menu body function toggleMenu() { menuHead.addEventListener("click", function() { if (menuBody.style.display === "none") { menuBody.style.display = "block"; } else { menuBody.style.display = "none"; } }); menuBody.addEventListener("click", function(ev) { if (ev.target !== ev.currentTarget) { menuHead.children[0].textContent = ev.target.textContent; } menuBody.style.display = "none"; }); } // init toggle listeners toggleMenu(); } } // init the element customElements.define("drop-down", DropDown); </script>
<my-counter></my-counter> <script> class MyCounter extends HTMLElement { constructor() { super(); this.count = 0; const style = ` *{font-size: 200%;} span{width: 4rem; display: inline-block; text-align: center;} button{width: 64px; height: 64px; border: none; border-radius: 10px; background-color: seagreen; color: white;} `; const html = ` <button id="dec">-</button> <span>${this.count}</span> <button id="inc">+</button> `; this.attachShadow({ mode: 'open' }); this.shadowRoot.innerHTML = ` <style> ${style} </style> ${html} `; this.buttonInc = this.shadowRoot.getElementById('inc'); this.buttonDec = this.shadowRoot.getElementById('dec'); this.spanValue = this.shadowRoot.querySelector('span'); this.inc = this.inc.bind(this); this.dec = this.dec.bind(this); } inc() { this.count++; this.update(); } dec() { this.count--; this.update(); } update() { this.spanValue.innerText = this.count; } connectedCallback() { this.buttonInc.addEventListener('click', this.inc); this.buttonDec.addEventListener('click', this.dec); } disconnectedCallback() { this.buttonInc.removeEventListener('click', this.inc); this.buttonDec.removeEventListener('click', this.dec); } } customElements.define('my-counter', MyCounter); </script> </pre>
0 seconds
Lonely, unstyled paragraph.
<div> <template id="cool-timer"> <style> p.timer-display { display: block; padding: 2w; border-radius: 0.5vw; font-size: 3vw; border: 1px solid #212121; color: #212121; background: #fff; } p {background: #eb6; color: #212121; padding: 0.5vw; } </style> <p class="timer-display"><span id="timer">0</span> seconds</p> <p> <!-- A custom description that can be overridden in markup --> <slot name="timer-description">A cool timer worth the attention!</slot> </p> </template> <!-- Actual, rendered instance of the cool timer --> <cool-timer><span slot="timer-description">Lap 1 time</span></cool-timer> <p>Lonely, unstyled paragraph.</p> </div> <script> class CoolTimer extends HTMLElement { constructor(){ super(); const template = document.getElementById("cool-timer"); const templateContent = template.content; const shadowRoot = this.attachShadow({ mode: "open" }).appendChild( templateContent.cloneNode(true)); } // Called when the element is first connected to the DOM connectedCallback() { const timerDisplay = this.shadowRoot.getElementById("timer"); let elapsedSeconds = 0; // Every second, increment elapsed seconds and update timer display this.timer = setInterval(() => { elapsedSeconds++; timerDisplay.innerHTML = elapsedSeconds; }, 1000); } // Called when custom element is removed disconnectedCallback() { if (this.timer) { clearInterval(this.timer); this.timer = null; } } } customElements.define("cool-timer", CoolTimer); </script>